/*
 * Copyright (c) 2018-2024 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include <unistd.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>

#include "common.h"

int timeLevel = 0;

static pthread_mutex_t mutexLog = PTHREAD_MUTEX_INITIALIZER;
static FILE *sfileLogFile = NULL;
static int sintDebugLevel = WARNING;
static int sintLogClass = 0;
static char *logFileName;
static int sintIgnoreLevel;

static int get_log_level(void);
static char *logclass2string(int, char *);

void
log_message(enum log_level loglevel, enum log_class logclass, const char * fmt, ...)
{
    if (get_log_level() < loglevel || (0 < sintIgnoreLevel && sintIgnoreLevel <= loglevel)) {
        return;
    }

    if (loglevel >= INFO && logclass != NOCLASS && !(gf_get_logclass() & logclass)) {
        return;
    }

    if (sfileLogFile != NULL) {
        pthread_mutex_lock(&mutexLog);
    }

    va_list argptr;
    char logBuf[PATH_MAX];

    va_start(argptr, fmt);
    vsnprintf(logBuf, sizeof(logBuf), fmt, argptr);
    va_end(argptr);

    time_t now;
    now = time(NULL);
    char logLevelString[256];
    char logClassString[256];

    if (NULL != sfileLogFile) {
        char timebuf[TIMESTRINGLEN];
        struct stat st;

        if (stat(logFileName, &st) < 0 && errno == ENOENT) {
            char *reopenFile = copy_string(logFileName);

            close_log();
            int ret;
            ret = open_log(reopenFile, loglevel2string(sintDebugLevel, logLevelString), timeLevel);
            FREEUP(reopenFile);
            if (ret < 0) {
                return;
            }
        }

        fprintf(sfileLogFile, "%s %d thread <%ld> %s %s ", ctime_2string(CTIME_FORMAT_a_b_d_T_Y, &now, timebuf),
                getpid(), pthread_self(),
                loglevel2string(loglevel, logLevelString),
                logclass2string(logclass, logClassString));

        fprintf(sfileLogFile, "%s\n", logBuf);
        fflush(sfileLogFile);

        pthread_mutex_unlock(&mutexLog);
    } else {
        fprintf(stderr, "%s\n", logBuf);
    }

    return;
}

int
open_log(const char *logfile, const char *debug, unsigned int timelevel)
{
    if (NULL != sfileLogFile) {
        return 0;
    }

    sfileLogFile = fopen(logfile, "a");
    if (NULL == sfileLogFile) {
        printf("Open log file <%s> failed due to: %s.\n", logfile, strerror(errno));
        return -1;
    }

    char daemon[PATH_MAX];
    daemon[0] = '\0';
    char *p;
    if ((p = strrchr((char *)logfile, '/')) != NULL) {
        strcpy(daemon, p + 1);
    } else {
        strcpy(daemon, logfile);
    }

    if ((p = strchr(daemon, '.')) != NULL) {
        *p = '\0';
    }

    /* for ee log, chmod to 0666 */
    if (strcmp(daemon, "ee") == 0) {
        chmod(logfile, 0666);
    }

    logFileName = copy_string(logfile);
    gf_set_loglevel(debug);
    gf_set_timinglog(timelevel);

    return 0;
}

void
close_log(void)
{
    if (NULL == sfileLogFile) {
        return;
    }

    FREEUP(logFileName);
    fclose(sfileLogFile);
    sfileLogFile = NULL;

    return;
}

void
gf_set_ignorewarn(int level)
{
    sintIgnoreLevel = level;
    return;
}

void
gf_set_loglevel(const char *debug)
{
    if (strcmp(debug, "ERR") == 0) {
        sintDebugLevel = ERR;
    } else if (strcmp(debug, "WARNING") == 0) {
        sintDebugLevel = WARNING;
    } else if (strcmp(debug, "INFO") == 0) {
        sintDebugLevel = INFO;
    } else if (strcmp(debug, "DEBUG") == 0) {
        sintDebugLevel = DEBUG;
    } else if (strcmp(debug, "DEBUG2") == 0) {
        sintDebugLevel = DEBUG2;
    } else if (strcmp(debug, "SILENT") == 0) {
        sintDebugLevel = NOLOG;
    } else {
        sintDebugLevel = INFO;
    }

    return;
}

void
gf_set_timinglog(unsigned int timelevel)
{
    timeLevel = timelevel;

    return;
}

void
gf_set_intlogclass(int logclass)
{
    sintLogClass = logclass;

    return;
}

int
gf_set_logclass(const char *classString)
{
    sintLogClass = 0;

    if (classString == NULL) {
        return 0;
    }

    if (strstr(classString, "TRACER") != NULL) {
        sintLogClass |= TRACER;
    }
    if (strstr(classString, "PERFM") != NULL) {
        sintLogClass |= PERFM;
    }
    if (strstr(classString, "CHANNEL") != NULL) {
        sintLogClass |= CHANNEL;
    }
    if (strstr(classString, "SCHEDULE") != NULL) {
        sintLogClass |= SCHEDULE;
    }
    if (strstr(classString, "DEBUGXDR") != NULL) {
        sintLogClass |= DEBUGXDR;
    }
    if (strstr(classString, "DEBUGRES") != NULL) {
        sintLogClass |= DEBUGRES;
    }
    if (strstr(classString, "EXECUTE") != NULL) {
        sintLogClass |= EXECUTE;
    }
    if (strstr(classString, "DEBUGSIGNAL") != NULL) {
        sintLogClass |= DEBUGSIGNAL;
    }
    if (strstr(classString, "PENDINGREASON") != NULL) {
        sintLogClass |= PENDINGREASON;
    }
    if (strstr(classString, "FAIRSHARE") != NULL) {
        sintLogClass |= FAIRSHARE;
    }
    if (strstr(classString, "PREEMPTION") != NULL) {
        sintLogClass |= PREEMPTION;
    }
    if (strstr(classString, "TASKSTART") != NULL) {
        sintLogClass |= TASKSTART;
    }
    if (strstr(classString, "EVENTFILE") != NULL) {
        sintLogClass |= EVENTFILE;
    }
    if (strstr(classString, "JOBGROUP") != NULL) {
        sintLogClass |= JOBGROUP;
    }

    return sintLogClass;
}

static int
get_log_level(void) 
{
    return sintDebugLevel;
}

int
gf_get_logclass(void) 
{
    return sintLogClass;
}

char * 
loglevel2string(int logLevel, char *levelString)
{
    if (logLevel == ERR) {
        strcpy(levelString, "ERR");
    } else if (logLevel == WARNING) {
        strcpy(levelString, "WARNING");
    } else if (logLevel == INFO) {
        strcpy(levelString, "INFO");
    } else if (logLevel == DEBUG) {
        strcpy(levelString, "DEBUG");
    }else if (logLevel == DEBUG2) {
        strcpy(levelString, "DEBUG2");
    } else {
        strcpy(levelString, "UNKWN");
    }

    return levelString;
}

static char * 
logclass2string(int logclass, char *logclassString)
{
    int isLogClassSet = FALSE;

    if (logclass & TRACER) {
        strcpy(logclassString, "TRACER");
        isLogClassSet = TRUE;
    }
    if (logclass & PERFM) {
        if (!isLogClassSet) {
            strcpy(logclassString, "PERFM");
        } else {
            strcat(logclassString, "|PERFM");
        }
        isLogClassSet = TRUE;
    } 
    if (logclass & CHANNEL) {
        if (!isLogClassSet) {
            strcpy(logclassString, "CHANNEL");
        } else {
            strcat(logclassString, "|CHANNEL");
        }
        isLogClassSet = TRUE;
    }
    if (logclass & SCHEDULE) {
        if (!isLogClassSet) {
            strcpy(logclassString, "SCHEDULE");
        } else {
            strcat(logclassString, "|SCHEDULE");
        }
        isLogClassSet = TRUE;
    }
    if (logclass & DEBUGXDR) {
        if (!isLogClassSet) {
            strcpy(logclassString, "DEBUGXDR");
        } else {
            strcat(logclassString, "|DEBUGXDR");
        }
        isLogClassSet = TRUE;
    }
    if (logclass & DEBUGRES) {
        if (!isLogClassSet) {
            strcpy(logclassString, "DEBUGRES");
        } else {
            strcat(logclassString, "|DEBUGRES");
        }
        isLogClassSet = TRUE;
    }
    if (logclass & DEBUGSIGNAL) {
        if (!isLogClassSet) {
            strcpy(logclassString, "DEBUGSIGNAL");
        } else {
            strcat(logclassString, "|DEBUGSIGNAL");
        }
        isLogClassSet = TRUE;
    }
    if (logclass & EXECUTE) {
        if (!isLogClassSet) {
            strcpy(logclassString, "EXECUTE");
        } else {
            strcat(logclassString, "|EXECUTE");
        }
        isLogClassSet = TRUE;
    }
    if (logclass & PENDINGREASON) {
        if (!isLogClassSet) {
            strcpy(logclassString, "PENDINGREASON");
        } else {
            strcat(logclassString, "|PENDINGREASON");
        }
        isLogClassSet = TRUE;
    }
    if (logclass & FAIRSHARE) {
        if (!isLogClassSet) {
            strcpy(logclassString, "FAIRSHARE");
        } else {
            strcat(logclassString, "|FAIRSHARE");
        }
        isLogClassSet = TRUE;
    }
    if (logclass & EVENTFILE) {
        if (!isLogClassSet) {
            strcpy(logclassString, "EVENTFILE");
        } else {
            strcat(logclassString, "|EVENTFILE");
        }
        isLogClassSet = TRUE;
    }
    if (logclass & PREEMPTION) {
        if (!isLogClassSet) {
            strcpy(logclassString, "PREEMPTION");
        } else {
            strcat(logclassString, "|PREEMPTION");
        }
        isLogClassSet = TRUE;
    }
    if (logclass & TASKSTART) {
        if (!isLogClassSet) {
            strcpy(logclassString, "TASKSTART");
        } else {
            strcat(logclassString, "|TASKSTART");
        }
        isLogClassSet = TRUE;
    }
    if (logclass & JOBGROUP) {
        if (!isLogClassSet) {
            strcpy(logclassString, "JOBGROUP");
        } else {
            strcat(logclassString, "|JOBGROUP");
        }
        isLogClassSet = TRUE;
    }
    if (!isLogClassSet) {
        strcpy(logclassString, "NOCLASS");
    }

    return logclassString;
}
